/*
 * Copyright 2008 Nathan C Jones
 * 
 * This file is part of Fivehundred.
 * 
 * Fivehundred is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 * 
 * Fivehundred is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * Fivehundred. If not, see <http://www.gnu.org/licenses/>. 
 */

package com.googlecode.fivehundred.model;

import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;

/**
 * A basic {@link CardCollection} suitable as a super type of any card
 * collection which internally uses a <code>java.util.Collection</code>.
 * 
 * @author Nathan C Jones
 */
public abstract class AbstractCardCollection implements CardCollection {

	/** The registered card collection listeners. */
	protected final Collection<CardCollectionListener> listeners = new LinkedList<CardCollectionListener>();

	/**
	 * Get the internal collection used by this card collection to store the
	 * cards.
	 * 
	 * @return the internal card collection.
	 */
	protected abstract Collection<Card> getCards();

	/**
	 * {@inheritDoc}
	 */
	@Override
	public final void addCardCollectionListener(final CardCollectionListener listener) {
		listeners.add(listener);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public final void removeCardCollectionListener(final CardCollectionListener listener) {
		listeners.remove(listener);
	}

	/**
	 * Add a card to the collection. The position that the card gets added to is
	 * up to the implementation.
	 * 
	 * @param card the card to be added to the card collection.
	 * @throws IllegalArgumentException if the card is <code>null</code>.
	 */
	@Override
	public final boolean add(final Card card) {
		if (card == null) {
			throw new IllegalArgumentException("Cannot add a null card.");
		}
		final boolean added = getCards().add(card);
		if (added) {
			fireCardAdded(card);
		}
		return added;
	}

	/**
	 * Notify all registered card collection listeners that a card has been
	 * added.
	 * 
	 * @param card the card that has been added.
	 */
	protected final void fireCardAdded(final Card card) {
		for (final CardCollectionListener listener : listeners) {
			listener.cardAdded(card, this);
		}
	}

	/**
	 * Remove a card from the collection.
	 * 
	 * @param card the card to remove.
	 * @throws IllegalArgumentException if the card is <code>null</code>.
	 */
	@Override
	public boolean remove(final Object card) {
		if (card == null) {
			throw new IllegalArgumentException("Cannot remove a null card.");
		}
		final boolean removed = getCards().remove(card);
		if (removed) {
			fireCardRemoved((Card) card);
		}
		return removed;
	}

	/**
	 * Notify all registered card collection listeners that a card has been
	 * removed.
	 * 
	 * @param card the card that has been removed.
	 */
	protected final void fireCardRemoved(final Card card) {
		for (final CardCollectionListener listener : listeners) {
			listener.cardRemoved(card, this);
		}
	}

	/**
	 * Removes all cards from the collection and notifies observers of each
	 * removal.
	 */
	@Override
	public void clear() {
		for (final Card card : this) {
			remove(card);
			fireCardRemoved(card);
		}
	}

	/**
	 * Check if a card is contained in this collection.
	 */
	@Override
	public boolean contains(final Object card) {
		return getCards().contains(card);
	}

	/**
	 * Check if this collection contains all the cards from the given
	 * collection.
	 */
	@Override
	public boolean containsAll(final Collection<?> c) {
		return getCards().containsAll(c);
	}

	/**
	 * @return <code>true</code> if there are no cards in the Collection.
	 */
	@Override
	public boolean isEmpty() {
		return getCards().isEmpty();
	}

	/**
	 * Get the size of the card collection.
	 * 
	 * @return the number of cards in the card collection.
	 */
	@Override
	public int size() {
		return getCards().size();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Object[] toArray() {
		return getCards().toArray();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public <T> T[] toArray(final T[] a) {
		return getCards().toArray(a);
	}

	/**
	 * Get an iterator for the cards in the collection.
	 * 
	 * @return the card collection's iterator.
	 */
	@Override
	public final Iterator<Card> iterator() {
		return Collections.unmodifiableCollection(getCards()).iterator();
	}

	/**
	 * Not supported.
	 * 
	 * @throws UnsupportedOperationException
	 */
	@Override
	public boolean addAll(final Collection<? extends Card> c) {
		throw new UnsupportedOperationException("addAll not supported");
	}

	/**
	 * Not supported.
	 * 
	 * @throws UnsupportedOperationException
	 */
	@Override
	public boolean removeAll(final Collection<?> c) {
		throw new UnsupportedOperationException("removeAll not supported");
	}

	/**
	 * Not supported.
	 * 
	 * @throws UnsupportedOperationException
	 */
	@Override
	public boolean retainAll(final Collection<?> c) {
		throw new UnsupportedOperationException("retainAll not supported");
	}

}
